//
//  Checkout.swift
//  JKTool
//
//  Created by 姜奎 on 2022/6/23.
//

import Foundation
extension JKTool.Git {
    struct Checkout: ParsableCommand {
        static var configuration = CommandConfiguration(
            commandName: "checkout",
            _superCommandName: "git",
            abstract: "checkout")
        
        @Option(name: .shortAndLong, help: "Checkout branch")
        var branch: String
        
        @Option(name: .shortAndLong, help: "兜底分支")
        var currentBranch: String?
        
        @Option(name: .shortAndLong, help: "remote-name，default：origin")
        var upstream: String?
        
        @Option(name: .shortAndLong, help: "递归子模块，default：false")
        var recursive: Bool = false
        
        @Option(name: .shortAndLong, help: "强制 checkout，default：false")
        var force: Bool = false
        
        @Option(name: .shortAndLong, help: "遇到错误时终止，default：false")
        var exitError: Bool = false
        
        @Option(name: .shortAndLong, help: "执行路径")
        var path: String?
        
        mutating func run() {
            
            guard let project = Project.project(directoryPath: path ?? FileManager.default.currentDirectoryPath) else {
                return po(tip: "\(path ?? FileManager.default.currentDirectoryPath)目录没有检索到工程", type: .error)
            }
            
            guard project.rootProject == project, recursive == true else {
                checkout(project: project)
               return
            }
            
            po(tip: "======Checkout工程开始======", type: .tip)
            let date = Date.init().timeIntervalSince1970

            let pool = ThreadPool()

            for record in project.recordList {
                pool.addTask(priority: 1, handle: {[self] in
                    guard let pro = Project.project(directoryPath: "\(project.checkoutsPath)/\(record)") else {
                        po(tip: "\(record) 工程不存在，请检查 Modulefile.recordList 是否为最新内容",type: .exitError(exitError))
                        return
                    }
                    self.checkout(project: pro)
                })
            }

            pool.addTask(priority: 0) {[self] in
                checkout(project: project)
            }

            pool.start()
            po(tip: "======Checkout工程结束[\(GlobalConstants.duration(to: date) + " s")]======")
        }

        func checkout(project: Project){
            let date = Date.init().timeIntervalSince1970

            func tryCheckLocalBranch(branch: String) -> Bool {
                let command = ShellOutCommand.gitLocalBranch(branch: branch)
                let result = try? shellOut(to: command, at: project.directoryPath)
                let exist = !(result?.isEmpty ?? true)
                po(tip: "【\(project.directoryEntrance)】Local\(exist ?"":"不")存在branch[\(branch)]")
                return exist
            }

            func tryCheckOriginBranch(branch: String) -> Bool {

                do {
                    try shellOut(to: .gitFetchBranch(branch:branch, force: force), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】尝试Feach branch结束[\(branch)]")
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】尝试Feach branch[\(branch)]失败\n" + error.message + error.output,type: .warning)
                }

                let command = ShellOutCommand.gitOriginBranch(upStream: upstream,branch: branch)
                let result = try? shellOut(to: command, at: project.directoryPath)
                let exist = !(result?.isEmpty ?? true)
                po(tip: "【\(project.directoryEntrance)】Origin\(exist ?"":"不")存在branch[\(branch)]")
                return exist
            }

            func tryCheckLocalTag(tag: String) -> Bool {
                let command = ShellOutCommand.gitLocalTag(tag: tag)
                let result = try? shellOut(to: command, at: project.directoryPath)
                let exist = !(result?.isEmpty ?? true)
                po(tip: "【\(project.directoryEntrance)】Local\(exist ?"":"不")存在tag[\(tag)]")
                return exist
            }

            func tryCheckOriginTag(tag: String) -> Bool {

                 do {
                     try shellOut(to: .gitFetchTag(tag: tag, force: force), at: project.directoryPath)
                     po(tip: "【\(project.directoryEntrance)】尝试Feach tag结束[\(tag)]")
                 } catch {
                     let error = error as! ShellOutError
                     po(tip: "【\(project.directoryEntrance)】尝试Feach tag[\(tag)]失败\n" + error.message + error.output,type: .warning)
                 }

                let command = ShellOutCommand.gitOriginTag(upStream: upstream ,tag: tag)
                let result = try? shellOut(to: command, at: project.directoryPath)
                let exist = !(result?.isEmpty ?? true)
                po(tip: "【\(project.directoryEntrance)】Origin\(exist ?"":"不")存在tag[\(tag)]")
                return exist
            }

            if tryCheckLocalBranch(branch: branch) {

                if force {
                    do {
                        po(tip: "【\(project.directoryEntrance)】临时切换到\(currentBranch ?? "master")")
                        try shellOut(to: .gitSwitch(branch: currentBranch ?? "master", force: force), at: project.directoryPath)
                        po(tip: "【\(project.directoryEntrance)】删除本地分支[\(branch)]")
                        try shellOut(to: .gitDelLocalBranch(branch: branch, force: true), at: project.directoryPath)
                        po(tip: "【\(project.directoryEntrance)】开始拉取远程分支[\(branch)]")
                        try shellOut(to: .gitCheckout(branch: branch, force: true), at: project.directoryPath)
                        po(tip: "【\(project.directoryEntrance)】Switch branch[\(branch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                    } catch {
                        let error = error as! ShellOutError
                        po(tip: "【\(project.directoryEntrance)】Switch branch[\(branch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                    }
                } else {
                    do {
                        try shellOut(to: .gitSwitch(branch: branch, force: false), at: project.directoryPath)
                        po(tip: "【\(project.directoryEntrance)】Switch branch[\(branch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                    } catch {
                        let error = error as! ShellOutError
                        po(tip: "【\(project.directoryEntrance)】Switch branch[\(branch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                    }
                    JKTool.Git.Pull.main(["--exit-error",String(exitError),"--path",project.directoryPath])
                    return
                }
            }

            if tryCheckOriginBranch(branch: branch) {
                do {
                    try shellOut(to: .gitCheckout(branch: branch, force: force), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】Checkout branch[\(branch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】Checkout branch[\(branch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                }
                return
            }

            do {
                try shellOut(to: .gitFetchTag(tag: branch), at: project.directoryPath)
                po(tip: "【\(project.directoryEntrance)】fetch tag完成")
            } catch {
                let error = error as! ShellOutError
                po(tip: "【\(project.directoryEntrance)】fetch tag失败\n" + error.message + error.output, type: .warning)
                _ = try? shellOut(to: .gitDelTag(tag: branch, push: false), at: project.directoryPath)
                po(tip: "【\(project.directoryEntrance)】清理本地同名tag, 随后尝试重新fetch tag\n")
                do {
                    try shellOut(to: .gitFetchTag(tag: branch), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】fetch tag完成")
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】fetch tag重试失败。尝试fetch all\n" + error.message + error.output, type: .warning)
                    do {
                        try shellOut(to: .gitFetchAll(force: force), at: project.directoryPath)
                        po(tip: "【\(project.directoryEntrance)】fetch all完成")
                    } catch {
                        let error = error as! ShellOutError
                        po(tip: "【\(project.directoryEntrance)】fetch all失败\n" + error.message + error.output,type: .exitError(exitError))
                    }
                }
            }

            if tryCheckLocalTag(tag: branch) {
                do {
                    try shellOut(to: .gitSwitch(tag: branch, force: force), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】Switch tag[\(branch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】Switch tag[\(branch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                }
                return
            }

            if tryCheckOriginTag(tag: branch) {

                do {
                    try shellOut(to: .gitSwitch(tag: branch, force: force), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】Switch tag[\(branch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】Switch tag[\(branch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                }
                return
            }

            guard let currentBranch = currentBranch else {
                po(tip: "【\(project.directoryEntrance)】没有检索到branch/tag[\(branch)]，没有指定--current-branch，仅执行Pull操作", type: .warning)

                JKTool.Git.Pull.main(["--exit-error",String(exitError),"--path",project.directoryPath])
                return
            }

            if tryCheckLocalBranch(branch: currentBranch) {
                do {
                    try shellOut(to: .gitSwitch(branch: currentBranch, force: force), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】Switch currentBranch[\(currentBranch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】Switch currentBranch[\(currentBranch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                }
                JKTool.Git.Pull.main(["--exit-error",String(exitError),"--path",project.directoryPath])
                return
            }

            if tryCheckOriginBranch(branch: currentBranch) {
                do {
                    try shellOut(to: .gitCheckout(branch: currentBranch, force: force), at: project.directoryPath)
                    po(tip: "【\(project.directoryEntrance)】Checkout currentBranch[\(currentBranch)]完成[\(GlobalConstants.duration(to: date) + " s")]", type: .tip)
                } catch {
                    let error = error as! ShellOutError
                    po(tip: "【\(project.directoryEntrance)】Checkout currentBranch[\(currentBranch)]失败\n" + error.message + error.output,type: .exitError(exitError))
                }
                return
            }
            po(tip: "【\(project.directoryEntrance)】没有检索到branch/tag[\(branch)]，没有检索到--current-branch[\(currentBranch)]，仅执行Pull操作", type: .warning)
            JKTool.Git.Pull.main(["--exit-error",String(exitError),"--path",project.directoryPath])
        }
    }
}
